/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright(c) 2020 - 2023 Allwinner Technology Co.,Ltd. All rights reserved. */
/*
 * Copyright (C) 2015 Allwinnertech, z.q <zengqi@allwinnertech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include"disp_eink.h"

#ifdef SUPPORT_EINK
#include "include.h"
#include "disp_private.h"

/* #define BUFFER_SAVE_BACK */

#ifdef BUFFER_SAVE_BACK
#if defined(__DISP_TEMP_CODE__)
static s32 __clean_ring_queue(struct eink_buffer_manager *buffer_mgr)
{
	int i;
	unsigned int buf_size;

	buf_size = buffer_mgr->buf_size;

	for (i = 0; i < IMAGE_BUF_NUM; i++)
		disp_free((void *)buffer_mgr->image_slot[i].vaddr,
			  (void *)buffer_mgr->image_slot[i].paddr, buf_size);

	return 0;
}
#endif
static int __save_buf2storg(__u8 *buf, char *file_name, __u32 length,
			    loff_t pos)
{
	struct file *fp = NULL;
	mm_segment_t old_fs;
	ssize_t ret = 0;

	if ((buf == NULL) || (file_name == NULL)) {
		DE_DEBUG("buf or file_name is null\n");
		return -1;
	}

	fp = filp_open(file_name, O_RDWR | O_CREAT, 0644);
	if (IS_ERR(fp)) {
		DE_DEBUG("fail to open file(%s), ret=%d\n",
			file_name, (u32) fp);
		return -1;
	}

	old_fs = get_fs();
	set_fs(KERNEL_DS);

	ret = vfs_write(fp, buf, length, &pos);
	DE_DEBUG("save %s done, len=%d, pos=%lld, ret=%d\n",
			file_name, length, pos, ret);

	set_fs(old_fs);
	filp_close(fp, NULL);

	return ret;

}
int __put_gary2buf(__u8 *buf, char *file_name, __u32 length, loff_t pos)
{
	struct file *fp = NULL;
	mm_segment_t fs;
	__s32 read_len = 0;
	ssize_t ret = 0;

	if ((buf == NULL) || (file_name == NULL)) {
		DE_DEBUG("buf or file_name is null\n");
		return -1;
	}

	fp = filp_open(file_name, O_RDONLY, 0);
	if (IS_ERR(fp)) {
		 DE_WARN("fail to open file(%s), ret=%d\n", file_name, (u32)fp);
		return -1;
	}
	fs = get_fs();
	set_fs(KERNEL_DS);

	read_len = vfs_read(fp, buf, length, &pos);
	if (read_len != length) {
		DE_WARN("maybe miss some data(read=%d byte, file=%d byte)\n",
				read_len, length);
		ret = -EAGAIN;
	}
		set_fs(fs);
		filp_close(fp, NULL);

	return ret;

}
#endif

s32 __clear_ring_queue_image(struct eink_buffer_manager *buffer_mgr)
{
	int ret = 0, i = 0;

	mutex_lock(&buffer_mgr->mlock);

	for (i = 0; i < IMAGE_BUF_NUM; i++) {
		/* init mode need buf data 0xff */
		memset((void *)buffer_mgr->image_slot[i].vaddr,
		       0xff, buffer_mgr->buf_size);
	}

	mutex_unlock(&buffer_mgr->mlock);

	return ret;
}

bool __is_ring_queue_full(struct eink_buffer_manager *buffer_mgr)
{
	bool ret;
	unsigned int in_index, out_index;

	mutex_lock(&buffer_mgr->mlock);

	in_index = buffer_mgr->in_index;
	out_index = buffer_mgr->out_index;
	ret = ((in_index + 1) % IMAGE_BUF_NUM == out_index) ? true : false;

	mutex_unlock(&buffer_mgr->mlock);

	return ret;
}

bool __is_ring_queue_empty(struct eink_buffer_manager *buffer_mgr)
{
	bool ret;
	unsigned int in_index, out_index;

	mutex_lock(&buffer_mgr->mlock);

	in_index = buffer_mgr->in_index;
	out_index = buffer_mgr->out_index;
	ret = (in_index == (out_index + 1) % IMAGE_BUF_NUM) ? true : false;

	mutex_unlock(&buffer_mgr->mlock);

	return ret;
}

struct eink_8bpp_image *__get_current_image(struct eink_buffer_manager
					    *buffer_mgr)
{
	struct eink_8bpp_image *ret;
	unsigned int out_index;

	mutex_lock(&buffer_mgr->mlock);

	out_index = buffer_mgr->out_index;
	if (out_index < IMAGE_BUF_NUM) {
		ret = &buffer_mgr->image_slot[(out_index + 1) % IMAGE_BUF_NUM];
		goto out;
	} else {
		DE_WARN("in_index larger than the max value\n");
		ret = NULL;
		goto out;
	}

out:
	mutex_unlock(&buffer_mgr->mlock);

	return ret;
}

struct eink_8bpp_image *__get_last_image(struct eink_buffer_manager *buffer_mgr)
{
	struct eink_8bpp_image *ret;
	unsigned int out_index;

	mutex_lock(&buffer_mgr->mlock);

	out_index = buffer_mgr->out_index;
	if (out_index < IMAGE_BUF_NUM) {
		ret = &buffer_mgr->image_slot[out_index];
		goto out;
	} else {
		DE_WARN("in_index larger than the max value\n");
		ret = NULL;
		goto out;
	}

out:
	mutex_unlock(&buffer_mgr->mlock);

	return ret;
}

#if !defined(SUPPORT_WB)
static int __conver_32bit_bmp_to_8bit(u8 *src_image_data, u32 width,
				      u32 height, u8 *dest_image_data)
{
	struct st_argb *tmp_src_data = NULL;
	u8 *tmp_dest_data = NULL;
	u32 wi = 0, hi = 0, co = 0;

	if ((src_image_data == NULL) || (dest_image_data == NULL)) {
		DE_WARN("input param is null\n");
		return -1;
	}

	tmp_dest_data = (u8 *) dest_image_data;
	hi = height;
	while (hi > 0) {
		tmp_src_data =
		    (struct st_argb *)src_image_data + (height - hi) * width;
		for (wi = 0; wi < width; wi++) {
			*tmp_dest_data = (306 * tmp_src_data->red +
					  601 * tmp_src_data->green +
					  117 * tmp_src_data->blue +
					  0x200) >> 10;
			tmp_dest_data++;
			tmp_src_data++;
			co++;
		}
		hi--;
	}
	DE_DEBUG("size = %u\n", co);

	return 0;
}
#endif

s32 __queue_image(struct eink_buffer_manager *buffer_mgr,
		  struct disp_layer_config_inner *config,
		  unsigned int layer_num,
		  u32 mode, struct area_info update_area)
{
	bool queue_is_full;
	int ret = 0;
	unsigned int in_index, out_index;
	unsigned int buf_size;
#ifndef SUPPORT_WB
	u8 *src_image;
#endif
#ifdef SUPPORT_WB
	struct format_manager *cmgr;
	struct image_format src, dest;
#endif

	mutex_lock(&buffer_mgr->mlock);

	in_index = buffer_mgr->in_index;
	out_index = buffer_mgr->out_index;
	buf_size = buffer_mgr->buf_size;

	if (in_index >= IMAGE_BUF_NUM) {
		DE_WARN("in_index larger than the max value\n");
		ret = -EINVAL;
		goto out;
	}

	queue_is_full =
	    ((in_index + 1) % IMAGE_BUF_NUM == out_index) ? true : false;
	/* when last image is missed ,it need cover last image */

	if (queue_is_full) {
		if (in_index)
			in_index--;
		else
			in_index = IMAGE_BUF_NUM - 1;
	}
#ifdef SUPPORT_WB
	cmgr = disp_get_format_manager(0);
	memset((void *)&src, 0, sizeof(struct image_format));
	src.format = DISP_FORMAT_ARGB_8888;
	src.width = buffer_mgr->width;
	src.height = buffer_mgr->height;

	memset((void *)&dest, 0, sizeof(struct image_format));
	dest.format = DISP_FORMAT_8BIT_GRAY;
	dest.addr1 = (unsigned long)buffer_mgr->image_slot[in_index].paddr;
	dest.width = buffer_mgr->width;
	dest.height = buffer_mgr->height;

	/* start convert */
	if (config != NULL)/* config regs */
		ret = cmgr->start_convert(0, config, layer_num, &dest);
#ifdef BUFFER_SAVE_BACK
	__save_buf2storg((void *)buffer_mgr->image_slot[in_index].vaddr,
			 "./dst_eink_image.bin",
			 buffer_mgr->width * buffer_mgr->height, 0);
#endif
	if (ret)
		goto out;
#else
	if (src_image != NULL)
		__conver_32bit_bmp_to_8bit(src_image, buffer_mgr->width,
					   buffer_mgr->height,
					   (void *)buffer_mgr->
					   image_slot[in_index].vaddr);

	__put_gary2buf((void *)buffer_mgr->image_slot[in_index].vaddr,
			"./system/eink_image.bin",
			buffer_mgr->width * buffer_mgr->height, 0);
#ifdef BUFFER_SAVE_BACK
	__save_buf2storg((void *)buffer_mgr->image_slot[in_index].vaddr,
			 "./eink_image.bin",
			 buffer_mgr->width * buffer_mgr->height, 0);
#endif

#endif				/* endif SUPPORT_WB */
	DE_DEBUG
	    ("index =%d, vaddr=%p, paddr=%p, xt=%d, yt=%d, xb=%d, yb=%d\n",
	     in_index, buffer_mgr->image_slot[in_index].vaddr,
	     buffer_mgr->image_slot[in_index].paddr, update_area.x_top,
	     update_area.y_top, update_area.x_bottom, update_area.y_bottom);
	/*
	 * update new 8bpp image information
	 */
	buffer_mgr->image_slot[in_index].state = USED;
	buffer_mgr->image_slot[in_index].update_mode = mode;
	buffer_mgr->image_slot[in_index].window_calc_enable = false;

	if (mode & EINK_RECT_MODE) {
		buffer_mgr->image_slot[in_index].flash_mode = LOCAL;

		if ((update_area.x_bottom == 0) && (update_area.x_top == 0) &&
		    (update_area.y_bottom == 0) && (update_area.y_top == 0)) {
			buffer_mgr->image_slot[in_index].window_calc_enable =
			    true;
			memset(&buffer_mgr->image_slot[in_index].update_area, 0,
			       sizeof(struct area_info));
		} else {
			memcpy(&buffer_mgr->image_slot[in_index].update_area,
			       &update_area, sizeof(struct area_info));
		}
	} else {
		if (mode == EINK_INIT_MODE)
			buffer_mgr->image_slot[in_index].flash_mode = INIT;
		else
			buffer_mgr->image_slot[in_index].flash_mode = GLOBAL;

		/* set update area full screen. */
		buffer_mgr->image_slot[in_index].update_area.x_top = 0;
		buffer_mgr->image_slot[in_index].update_area.y_top = 0;
		buffer_mgr->image_slot[in_index].update_area.x_bottom =
		    buffer_mgr->image_slot[in_index].size.width - 1;
		buffer_mgr->image_slot[in_index].update_area.y_bottom =
		    buffer_mgr->image_slot[in_index].size.height - 1;
	}

	/*
	 * if queue is full,then cover the newest image,
	 * and in_index keep the same value.
	 */
	if (!queue_is_full)
		buffer_mgr->in_index =
		    (buffer_mgr->in_index + 1) % IMAGE_BUF_NUM;

out:
	DE_DEBUG("q:in_index:%d, out_index:%d,paddr:0x%p, wcalc_en=%d\n",
		buffer_mgr->in_index, buffer_mgr->out_index,
		buffer_mgr->image_slot[in_index].paddr,
		buffer_mgr->image_slot[in_index].window_calc_enable);

	mutex_unlock(&buffer_mgr->mlock);

	return ret;
}

s32 __dequeue_image(struct eink_buffer_manager *buffer_mgr)
{
	bool queue_is_empty;
	int ret = 0;
	unsigned int in_index, out_index;

	mutex_lock(&buffer_mgr->mlock);
	in_index = buffer_mgr->in_index;
	out_index = buffer_mgr->out_index;

	if (out_index >= IMAGE_BUF_NUM) {
		DE_WARN("out_index larger than the max value\n");
		ret = -EINVAL;
		goto out;
	}

	queue_is_empty =
	    (in_index == (out_index + 1) % IMAGE_BUF_NUM) ? true : false;
	if (queue_is_empty) {
		ret = -EBUSY;
		DE_DEBUG("queue is empty\n");
		goto out;
	}

	/* set the 8pp image information to initial state. */
	buffer_mgr->image_slot[out_index].state = FREE;
	buffer_mgr->image_slot[out_index].flash_mode = GLOBAL;
	buffer_mgr->image_slot[out_index].update_mode = EINK_INIT_MODE;
	buffer_mgr->image_slot[out_index].window_calc_enable = true;
	buffer_mgr->out_index = (buffer_mgr->out_index + 1) % IMAGE_BUF_NUM;

	DE_DEBUG("dq in_index:%d, out_index:%d\n", buffer_mgr->in_index,
		buffer_mgr->out_index);
out:
	mutex_unlock(&buffer_mgr->mlock);

	return ret;
}

s32 ring_buffer_manager_init(struct disp_eink_manager *eink_manager)
{
	int i;
	int ret = 0;
	struct eink_buffer_manager *buffer_mgr = NULL;

	buffer_mgr =
		(struct eink_buffer_manager *)disp_sys_malloc(
				sizeof(struct eink_buffer_manager));
	if (!buffer_mgr) {
		DE_WARN("malloc eink buffer manager memory fail\n");
		ret = -ENOMEM;
		goto buffer_mgr_err;
	}

	/*
	 * init in_index is 1,ring buffer is empty.
	 * out_index is 0,point to last image.
	 */
	memset((void *)(buffer_mgr), 0, sizeof(struct eink_buffer_manager));
	buffer_mgr->width = eink_manager->private_data->param.timing.width;
	buffer_mgr->height = eink_manager->private_data->param.timing.height;
	buffer_mgr->buf_size = buffer_mgr->width * buffer_mgr->height;
	buffer_mgr->in_index = 1;
	buffer_mgr->out_index = 0;
	mutex_init(&buffer_mgr->mlock);

	for (i = 0; i < IMAGE_BUF_NUM; i++) {
		buffer_mgr->image_slot[i].size.width = buffer_mgr->width;
		buffer_mgr->image_slot[i].size.height = buffer_mgr->height;
		/* fix it to match different drawer. */
		buffer_mgr->image_slot[i].size.align = 4;
		buffer_mgr->image_slot[i].update_mode = EINK_INIT_MODE;
		buffer_mgr->image_slot[i].vaddr =
		    disp_malloc(buffer_mgr->buf_size,
				&buffer_mgr->image_slot[i].paddr);
		if (!buffer_mgr->image_slot[i].vaddr) {
			DE_WARN("malloc image buffer memory fail\n");
			ret = -ENOMEM;
			goto image_buffer_err;
		}
		DE_DEBUG("image paddr%d=0x%p", i,
			buffer_mgr->image_slot[i].paddr);

		/* init mode need buf data 0xff */
		memset((void *)buffer_mgr->image_slot[i].vaddr, 0xff,
		       buffer_mgr->buf_size);
	}

	buffer_mgr->is_full = __is_ring_queue_full;
	buffer_mgr->is_empty = __is_ring_queue_empty;
	buffer_mgr->get_last_image = __get_last_image;
	buffer_mgr->get_current_image = __get_current_image;
	buffer_mgr->queue_image = __queue_image;
	buffer_mgr->dequeue_image = __dequeue_image;
	buffer_mgr->clear_image = __clear_ring_queue_image;
	eink_manager->buffer_mgr = buffer_mgr;

	return ret;

image_buffer_err:
	for (i = 0; i < IMAGE_BUF_NUM; i++) {
		if (buffer_mgr->image_slot[i].vaddr)
			disp_free(buffer_mgr->image_slot[i].vaddr,
				  (void *)buffer_mgr->image_slot[i].paddr,
				  buffer_mgr->buf_size);
	}

buffer_mgr_err:
	kfree(buffer_mgr);

	return ret;
}

s32 ring_buffer_manager_exit(struct disp_eink_manager *eink_manager)
{
	int i;
	struct eink_buffer_manager *buffer_mgr = NULL;

	buffer_mgr = eink_manager->buffer_mgr;
	for (i = 0; i < IMAGE_BUF_NUM; i++) {
		if (buffer_mgr->image_slot[i].vaddr)
			disp_free(buffer_mgr->image_slot[i].vaddr,
				  (void *)buffer_mgr->image_slot[i].paddr,
				  buffer_mgr->buf_size);
	}

	kfree(buffer_mgr);
	eink_manager->buffer_mgr = NULL;

	return 0;
}
#endif
